﻿// ------------------------------------------------------------------------
// 版权信息
// 版权归重庆虫儿飞科技有限公司所有。
// 所有权利保留。
// 官方网站：https://netokit.com
// 许可证信息
// Neto.Kit 项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
// 
// 使用条款
// 使用本代码应遵守相关法律法规和许可证的要求。
// 
// 免责声明
// 对于因使用本代码而产生的任何直接、间接、偶然、特殊或后果性损害，我们不承担任何责任。
// 
// 其他重要信息
// Neto.Kit 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。
// 有关 Neto.Kit 项目的其他详细信息，请参阅位于源代码树根目录中的 COPYRIGHT 和 DISCLAIMER 文件。
// 
// 更多信息
// 请访问 https://netokit.com 获取更多关于 Neto.Kit 项目的许可证和版权信息。
// ------------------------------------------------------------------------

namespace Neto.Files;

/// <summary>
///     文件真实格式检查
/// </summary>
public static class FileTypeCheck
{
    /// <summary>
    ///     文件签名信息
    /// </summary>
    private static readonly Dictionary<string, List<byte[]>> fileSignature = new()
    {
        { ".DOC", new List<byte[]> { new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 } } },
        { ".DOCX", new List<byte[]> { new byte[] { 0x50, 0x4B, 0x03, 0x04 } } },
        { ".PDF", new List<byte[]> { new byte[] { 0x25, 0x50, 0x44, 0x46 } } },
        {
            ".ZIP",
            new List<byte[]>
            {
                new byte[] { 0x50, 0x4B, 0x03, 0x04 },
                new byte[] { 0x50, 0x4B, 0x4C, 0x49, 0x54, 0x55 },
                new byte[] { 0x50, 0x4B, 0x53, 0x70, 0x58 },
                new byte[] { 0x50, 0x4B, 0x05, 0x06 },
                new byte[] { 0x50, 0x4B, 0x07, 0x08 },
                new byte[] { 0x57, 0x69, 0x6E, 0x5A, 0x69, 0x70 }
            }
        },

        //图片格式
        { ".PNG", new List<byte[]> { new byte[] { 0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A } } },
        {
            ".JPG",
            new List<byte[]>
            {
                new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 },
                new byte[] { 0xFF, 0xD8, 0xFF, 0xE1 },
                new byte[] { 0xFF, 0xD8, 0xFF, 0xE8 }
            }
        },
        {
            ".JPEG",
            new List<byte[]>
            {
                new byte[] { 0xFF, 0xD8, 0xFF, 0xE0 },
                new byte[] { 0xFF, 0xD8, 0xFF, 0xE2 },
                new byte[] { 0xFF, 0xD8, 0xFF, 0xE3 }
            }
        },
        { ".GIF", new List<byte[]> { new byte[] { 0x47, 0x49, 0x46, 0x38 } } },
        { ".BMP", new List<byte[]> { new byte[] { 0x42, 0x4D } } },
        { ".JFIF", new List<byte[]> { new byte[] { 0xFF, 0xD8, 0xFF, 0xE0, 0x00, 0x10, 0x4A, 0x46, 0x49, 0x46 } } },

        //视频格式
        //高清，高占用空间
        {
            ".MP4",
            new List<byte[]>
            {
                new byte[] { 0x00, 0x00, 0x00, 0x18, 0x66, 0x74, 0x79, 0x70, 0x6D, 0x70, 0x34, 0x32 },
                new byte[] { 0x00, 0x00, 0x00, 0x1C, 0x66, 0x74, 0x79, 0x70, 0x6D, 0x70, 0x34, 0x32 },
                new byte[] { 0x00, 0x00, 0x00, 0x20, 0x66, 0x74, 0x79, 0x70, 0x69, 0x73, 0x6F, 0x6D }
            }
        },
        //高清，中度占用空间
        {
            ".MKV", new List<byte[]> { new byte[] { 0x1A, 0x45, 0xDF, 0xA3, 0xA3, 0x42, 0x86, 0x81, 0x01, 0x42, 0xF7 } }
        },
        //高清，中度占用空间
        { ".MOV", new List<byte[]> { new byte[] { 0x00, 0x00, 0x00, 0x14, 0x66, 0x74, 0x79, 0x70, 0x71, 0x74 } } },
        //高清，低占用空间
        {
            ".M4V", new List<byte[]> { new byte[] { 0x00, 0x00, 0x00, 0x20, 0x66, 0x74, 0x79, 0x70, 0x4D, 0x34, 0x56 } }
        },
        //高清，低占用空间
        {
            ".WEBM",
            new List<byte[]>
            {
                new byte[]
                {
                    0x1A, 0x45, 0xDF, 0xA3, 0x9F, 0x42, 0x86, 0x81, 0x01, 0x42, 0xF7, 0x81, 0x01, 0x42, 0xF2
                }
            }
        },
        //低质量
        { ".WMV", new List<byte[]> { new byte[] { 0x30, 0x26, 0xB2, 0x75, 0x8E, 0x66, 0xCF, 0x11 } } },
        //低质量
        {
            ".AVI", new List<byte[]> { new byte[] { 0x52, 0x49, 0x46, 0x46, 0x84, 0x4A, 0x1E, 0x00, 0x41, 0x56, 0x49 } }
        },
        //低质量
        { ".FLV", new List<byte[]> { new byte[] { 0x46, 0x4C, 0x56 } } },

        //电子表格
        {
            ".XLS",
            new List<byte[]>
            {
                new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 },
                new byte[] { 0x09, 0x08, 0x10, 0x00, 0x00, 0x06, 0x05, 0x00 },
                new byte[] { 0xFD, 0xFF, 0xFF, 0xFF }
            }
        },
        { ".XLSX", new List<byte[]> { new byte[] { 0x50, 0x4B, 0x03, 0x04 } } }
    };

    /// <summary>
    ///     文件真实格式检查
    /// </summary>
    /// <param name="filePath">文件完整路径</param>
    /// <returns></returns>
    public static bool IsValidFileExtension(string filePath)
    {
        using var file = File.OpenRead(filePath);
        using var memoryStream = new MemoryStream();
        file.CopyTo(memoryStream);
        var bytes = new byte[160];
        memoryStream.Position = 0;
        memoryStream.Read(bytes, 0, bytes.Length);

        var pass = IsValidFileExtension(filePath.GetExtension(), bytes);

        return pass;
    }

    /// <summary>
    ///     文件真实格式检查
    /// </summary>
    /// <param name="memoryStream">内存流</param>
    /// <param name="fileExtension">文件扩展名</param>
    /// <returns></returns>
    public static bool IsValidFileExtension(MemoryStream memoryStream, string fileExtension)
    {
        var bytes = new byte[160];
        memoryStream.Position = 0;
        memoryStream.Read(bytes, 0, bytes.Length);

        var pass = IsValidFileExtension(fileExtension, bytes);

        return pass;
    }

    /// <summary>
    ///     文件真实格式检查
    /// </summary>
    /// <param name="file">文件</param>
    /// <param name="fileExtension">文件扩展名</param>
    /// <returns></returns>
    public static bool IsValidFileExtension(IFormFile file, string fileExtension)
    {
        using var memoryStream = new MemoryStream();
        file.CopyTo(memoryStream);

        var bytes = new byte[160];
        memoryStream.Position = 0;
        memoryStream.Read(bytes, 0, bytes.Length);

        var pass = IsValidFileExtension(fileExtension, bytes);

        return pass;
    }

    /// <summary>
    ///     文件真实格式检查
    /// </summary>
    /// <param name="fileExtension">文件扩展名</param>
    /// <param name="fileData">文件头字节数组，请传200个以内</param>
    /// <param name="allowedChars">允许的例外字节数组</param>
    /// <returns></returns>
    public static bool IsValidFileExtension(string fileExtension, byte[] fileData, byte[] allowedChars = null)
    {
        if (string.IsNullOrEmpty(fileExtension) || fileData == null || fileData.Length == 0) return false;
        var flag = false;
        fileExtension = fileExtension.ToUpperInvariant();
        if (!fileSignature.TryGetValue(fileExtension, out var value)) return true;
        var sig = value;
        foreach (var b in sig)
        {
            var curFileSig = new byte[b.Length];
            Array.Copy(fileData, curFileSig, b.Length);
            if (!curFileSig.SequenceEqual(b)) continue;
            //验证格式通过
            flag = true;
            break;
        }

        return flag;
    }

    #region 第二种方法

    /// <summary>
    ///     获取文件签名
    /// </summary>
    /// <param name="filePath"></param>
    /// <returns></returns>
    public static string CheckTrueFileName(string filePath)
    {
        var fileclass = "";
        try
        {
            using var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read);
            using var reader = new BinaryReader(fs);
            for (var i = 0; i < 2; i++) fileclass += reader.ReadByte().ToString();
        }
        catch (Exception exc)
        {
            // Console.WriteLine(exc.Message);
        }

        return fileclass;
    }

    /// <summary>
    ///     文件扩展名检查
    /// </summary>
    /// <param name="filePath"></param>
    /// <returns></returns>
    public static bool IsValidFileExtension2(string filePath)
    {
        var extension = filePath.GetExtension().Trim('.').ToUpper();

        //如果没找到指定类型的枚举，则返回true，表示没有内置当前文件扩展的数据
        if (Enum.IsDefined(typeof(FileExtensionSignType), extension) == false) return true;
        //文件扩展的签名
        var extensionSign = (int)extension.ToEnum<FileExtensionSignType>();
        //文件的签名
        var sign = CheckTrueFileName(filePath);

        return extensionSign.ToString() == sign;
    }

    #endregion 第二种方法
}